package checker

import (
	"reflect"
	"kumachan/stdlib"
	"kumachan/interpreter/lang/common/name"
	"kumachan/interpreter/compiler/loader"
	"kumachan/interpreter/compiler/checker/typsys"
	"kumachan/interpreter/compiler/checker/checked"
)


type nominalType func(TypeRegistry)(typsys.Type)

const CoreModule = stdlib.Mod_core
const SelfModule = loader.SelfModuleImportName
const Discarded = "_"
const StringFormatterPlaceholder = '?'
const EffectFunctionName = "(effect)"
const F_Adapt = "@adapt"
const F_Validate = "@validate"
const F_Suffix_Serialize = "@serialize"
const F_Suffix_Deserialize = "@deserialize"
const F_Suffix_Default = "@default"
const T_Suffix_SrvCtorArg = "@Argument"

var typeBadNames = [] string {
	Discarded,
	typsys.TypeNameUnit,
	typsys.TypeNameUnknown,
	typsys.TypeNameTop,
	typsys.TypeNameBottom,
}
var functionBadNames = append([] string {}, typeBadNames...)

var coreFloat = makeCoreType(stdlib.Float)
var coreNormalFloat = makeCoreType(stdlib.NormalFloat)
var coreNormalFloat_name = makeCoreTypeName(stdlib.NormalFloat)
var coreNormalComplex_name = makeCoreTypeName(stdlib.NormalComplex)

var coreInteger = makeCoreType(stdlib.Integer)
var coreInteger_name = makeCoreTypeName(stdlib.Integer)
var coreNumber = makeCoreType(stdlib.Number)
var coreQword = makeCoreType(stdlib.Qword)
var coreDword = makeCoreType(stdlib.Dword)
var coreWord = makeCoreType(stdlib.Word)
var coreByte = makeCoreType(stdlib.Byte)

var coreBool = makeCoreType(stdlib.Bool)
var coreBool_name = makeCoreTypeName(stdlib.Bool)
const coreYesIndex = stdlib.YesIndex
const coreNoIndex = stdlib.NoIndex

var coreChar = makeCoreType(stdlib.Char)
var coreString = makeCoreType(stdlib.String)
var coreString_name = makeCoreTypeName(stdlib.String)
var coreBytes = makeCoreType(stdlib.Bytes)
var coreBytes_name = makeCoreTypeName(stdlib.Bytes)
var coreError = makeCoreType(stdlib.Error)

var coreProjRef = makeCoreType2(stdlib.ProjRef)
var coreProjRef_ = makeCoreTypeMatcher2(stdlib.ProjRef)
var coreCaseRef = makeCoreType2(stdlib.CaseRef)
var coreCaseRef_ = makeCoreTypeMatcher2(stdlib.CaseRef)
var coreAsync = makeCoreType2(stdlib.Async)
var coreObservable_name = makeCoreTypeName(stdlib.Observable)

var coreMaybe_ = makeCoreTypeMatcher1(stdlib.Maybe)
// var coreResult = makeCoreType2(stdlib.Result)

var coreList = makeCoreType1(stdlib.List)
var coreList_ = makeCoreTypeMatcher1(stdlib.List)
func getListInfo(item_t typsys.Type) checked.ListInfo {
	var info checked.ListInfo
	var item_reflect_t, ok = getListItemPrimitiveReflectType(item_t)
	if ok {
		info.Compact = true
		info.ItemType = item_reflect_t
	}
	return info
}
func getListItemPrimitiveReflectType(item_t typsys.Type) (reflect.Type, bool) {
	var def, args, ok = typeGetRefDefArgs(item_t)
	if !(ok) { return nil, false }
	if len(args) > 0 { return nil, false }
	var mod = def.Name.ModuleName
	var item = def.Name.ItemName
	if mod != stdlib.Mod_core { return nil, false }
	return stdlib.GetPrimitiveReflectType(item)
}

var coreAssetBinary = makeCoreType(stdlib.AssetBinary)
var coreAssetPNG = makeCoreType(stdlib.AssetPNG)
var coreAssetCSS = makeCoreType(stdlib.AssetCSS)
var coreAssetTTF = makeCoreType(stdlib.AssetTTF)
type Asset struct {
	MIME  string
	Path  string
	Data  [] byte
}

var rpcMethod_ = refTypeMatcher2 (
	name.MakeTypeName(stdlib.Mod_rpc, stdlib.Method_rpc),
)
var rpcMethodMulti_ = refTypeMatcher2 (
	name.MakeTypeName(stdlib.Mod_rpc, stdlib.MethodMulti_rpc),
)
var rpcServiceIdentifier = func(srv typsys.Type, ca typsys.Type) nominalType {
	var n = name.MakeTypeName(stdlib.Mod_rpc, stdlib.ServiceIdentifier_rpc)
	return makeNominalType(n, typeAsNominal(srv), typeAsNominal(ca))
}

var entryEffectType = coreAsync(typsys.UnitType {}, typsys.BottomType {})
func MatchObservable(t typsys.Type, reg TypeRegistry, mod string) (typsys.Type, typsys.Type, bool) {
	var def, exists = reg[coreObservable_name]
	if !(exists) { panic("something went wrong") }
	var s0 = typsys.StartInferring(def.Parameters)
	var a = typsys.MakeAssignContext(mod, s0)
	var ok, s1 = typsys.Assign(defType(def.TypeDef), t, a)
	if ok {
		var v_type, ok1 = s1.GetCurrentValue(&(def.Parameters[0]))
		if !(ok1) { panic("something went wrong") }
		var e_type, ok2 = s1.GetCurrentValue(&(def.Parameters[1]))
		if !(ok2) { panic("something went wrong") }
		return v_type, e_type, true
	} else {
		return nil, nil, false
	}
}


func isValidTypeItemName(name string) bool {
	for _, bad := range typeBadNames {
		if name == bad {
			return false
		}
	}
	return true
}
func isValidFunctionItemName(name string) bool {
	for _, bad := range functionBadNames {
		if name == bad {
			return false
		}
	}
	return true
}
func isValidAliasName(name string) bool {
	return isValidFunctionItemName(name) && isValidTypeItemName(name)
}

func makeCoreType(item string, args ...nominalType) nominalType {
	return makeNominalType(makeCoreTypeName(item), args...)
}
func makeCoreType1(item string) func(typsys.Type)(nominalType) {
	return func(p typsys.Type) nominalType {
		return makeNominalType (
			makeCoreTypeName(item),
			typeAsNominal(p),
		)
	}
}
func makeCoreType2(item string) func(typsys.Type,typsys.Type)(nominalType) {
	return func(p typsys.Type, q typsys.Type) nominalType {
		return makeNominalType (
			makeCoreTypeName(item),
			typeAsNominal(p),
			typeAsNominal(q),
		)
	}
}
func makeCoreTypeMatcher0(item string) func(typsys.Type)(bool) {
	return refTypeMatcher0(makeCoreTypeName(item))
}
func makeCoreTypeMatcher1(item string) func(typsys.Type)(typsys.Type, bool) {
	return refTypeMatcher1(makeCoreTypeName(item))
}
func makeCoreTypeMatcher2(item string) func(typsys.Type)(typsys.Type, typsys.Type, bool) {
	return refTypeMatcher2(makeCoreTypeName(item))
}
func makeCoreTypeName(item string) name.TypeName {
	return name.MakeTypeName(stdlib.Mod_core, item)
}

func makeNominalType(n name.TypeName, args ...nominalType) nominalType {
	return nominalType(func(reg TypeRegistry) typsys.Type {
		var def, exists = reg[n]
		if !(exists) { panic("something went wrong") }
		var args_types = make([] typsys.Type, len(args))
		for i, arg := range args {
			args_types[i] = arg(reg)
		}
		return &typsys.NestedType {
			Content: typsys.Ref {
				Def:  def.TypeDef,
				Args: args_types,
			},
		}
	})
}
func typeAsNominal(t typsys.Type) nominalType {
	return nominalType(func(_ TypeRegistry) typsys.Type {
		return t
	})
}
func refTypeMatcher(n name.TypeName) func(typsys.Type)([] typsys.Type, bool) {
	return func(t typsys.Type) ([]typsys.Type, bool) {
		var ref, type_args, ok = typeGetRefDefArgs(t)
		if !(ok) { return nil, false }
		if ref.Name != n { return nil, false }
		return type_args, true
	}
}
func refTypeMatcher0(n name.TypeName) func(typsys.Type)(bool) {
	return func(t typsys.Type) bool {
		var _, ok = refTypeMatcher(n)(t)
		return ok
	}
}
func refTypeMatcher1(n name.TypeName) func(typsys.Type)(typsys.Type, bool) {
	return func(t typsys.Type) (typsys.Type, bool) {
		var type_args, ok = refTypeMatcher(n)(t)
		if !(ok) { return nil, false }
		return type_args[0], true
	}
}
func refTypeMatcher2(n name.TypeName) func(typsys.Type)(typsys.Type, typsys.Type, bool) {
	return func(t typsys.Type) (typsys.Type, typsys.Type, bool) {
		var type_args, ok = refTypeMatcher(n)(t)
		if !(ok) { return nil, nil, false }
		return type_args[0], type_args[1], true
	}
}


